#include "StdAfx.h"
#include "../sgp/Image.h"
#include "../sgp/VideoSurfaceHandle.h"
#include "../sgp/VideoSurfaceLock.h"
#include "../sgp/BlitFx.h"
#include "../sgp/VideoSurface.h"

namespace ja2{ namespace sgp
{
	VideoSurface::VideoSurface(std::uint_least8_t Depth, VideoSurfaceHandlePtr_t const &pHandle, MemoryUsage MemoryUsage_)
		: m_BitDepth(Depth), m_pHandle(pHandle), m_MemoryUsage(MemoryUsage_)
	{
		// Reserve region list size
		m_RegionList.reserve(DEFAULT_REGION_SIZE);
	}

	uint_least16_t VideoSurface::height() const
	{
		return m_pHandle->height();
	}

	uint_least16_t VideoSurface::width() const
	{
		return m_pHandle->width();
	}

	void VideoSurface::blit(VideoSurface &SrcSurface, std::int_least16_t DestX, std::int_least16_t DestY, std::uint_least32_t Flags, BlitFx *pBltFx)
	{
		// Check that both region and subrect are not given
		if((Flags & BF_SRC_REGION) && (Flags & BF_SRC_SUBRECT))
		{
			std::string const error = "Inconsistent blit flags given: BF_SRC_REGION | BF_SRC_SUBRECT.";
			JA2_DEBUG_MESSAGE(TOPIC_VIDEOSURFACE, DebugManager::DL_2, error.c_str());
			throw RuntimeException(error);
		}
		// If fill, fill entire region with color
		if ( Flags & BF_COLOR_FILL)
			m_pHandle->fill(pBltFx->ColorFill);
		// Fill rectangle with color
		else if ( Flags & BF_COLOR_FILL_RECT )
			m_pHandle->fill(pBltFx->ColorFill, pBltFx->FillRect);
		else
		// Check for source coordinate options - from region, specific rect or full src dimensions
		// Check for dest, src options
		if(Flags & BF_DEST_REGION )
		{
			JA2_ASSERT(pBltFx != nullptr);
			// Get region
			VideoSurfaceRegion const region = pBltFx->DestRegion;
			// Set starting coordinates from destination region
			DestY = region.RegionCoords.top();
			DestX = region.RegionCoords.left();
		}
		Rect src_rect;
		// Get Region if specified
		if (Flags & BF_SRC_REGION )
		{
			// Get region
			VideoSurfaceRegion const region = pBltFx->DestRegion;
			// Set rect
			src_rect = region.RegionCoords;
		}
		else
		{
			// Use SUBRECT if specified
			if(Flags & BF_SRC_SUBRECT)
				src_rect = pBltFx->SrcRect;
			else
			{
				// Here, use default, which is entire Video Surface
				// Check Sizes, SRC size MUST be <= DEST size
				if(height() < SrcSurface.height())
				{
					std::string const error = "Incompatible height size given in video Surface blit";
					JA2_DEBUG_MESSAGE(TOPIC_VIDEOSURFACE, DebugManager::DL_2, error.c_str());
					throw RuntimeException(error);
				}
				if(width() < SrcSurface.width())
				{
					std::string const error = "Incompatible height size given in video Surface blit.";
					JA2_DEBUG_MESSAGE(TOPIC_VIDEOSURFACE, DebugManager::DL_2, error.c_str());
					throw RuntimeException(error);
				}
				src_rect = Rect(0, 0, SrcSurface.width(), SrcSurface.height());
			}
		}
		// clipping -- added by DB
		std::uint_least16_t width = src_rect.right() - src_rect.left();
		std::uint_least16_t height = src_rect.bottom() - src_rect.top();
		// Get destination rect
		Rect const dest_rect = get_rect();
		// check for position entirely off the screen
		if(DestX >= dest_rect.right() || DestY >= dest_rect.bottom() || (DestX + width) < dest_rect.left() || (DestY + height) < dest_rect.top())
			JA2_DEBUG_MESSAGE(TOPIC_VIDEOSURFACE, DebugManager::DL_2, "Not blitting becasue at least one dimenstion is out of bound");
		else
		{
			// DB The mirroring stuff has to do it's own clipping because it needs to invert some of the numbers
			if(!(Flags & BF_MIRROR_Y))
			{
				if((DestX + width) >= dest_rect.right())
				{
					src_rect.set_right(src_rect.right() - (DestX + width - dest_rect.right()));
					width -= DestX + width - dest_rect.right();
				}
				if((DestY + height) >= dest_rect.bottom())
				{
					src_rect.set_bottom(src_rect.bottom() - (DestY + height - dest_rect.bottom()));
					height -= DestY + height - dest_rect.bottom();
				}
				if(DestX < dest_rect.left())
				{
					src_rect.set_left(src_rect.left() + dest_rect.left() - DestX);
					width -= dest_rect.left() - DestX;
					DestX = dest_rect.left();
				}
				if(DestY < dest_rect.top())
				{
					src_rect.set_top(src_rect.top() + dest_rect.top() - DestY);
					height -= dest_rect.top() - DestY;
					DestY = dest_rect.top();
				}
			}
			// BPP must be 32
			JA2_ASSERT(depth() == 32);
			// Blit
			blit_using_dd(SrcSurface, DestX, DestY, src_rect, Flags);
		}
	}

	void VideoSurface::blit_using_dd(VideoSurface &SrcSurface, std::uint_least16_t DestX, std::uint_least16_t DestY, Rect const &SrcRect, std::uint_least32_t Flags)
	{
		Rect src_rect(SrcRect);
		// Fast blitting
		if(Flags & BF_FAST )
		{
			// Convert flags into DD flags, ( for transparency use, etc )
			if(Flags & BF_USE_SRC_COLORKEY )
				m_pHandle->blit_fast_src_color_key(*SrcSurface.m_pHandle, DestX, DestY, src_rect);
			// Convert flags into DD flags, ( for transparency use, etc )
			else if(Flags & BF_USE_DEST_COLORKEY )
				m_pHandle->blit_fast_dest_color_key(*SrcSurface.m_pHandle, DestX, DestY, src_rect);
			// Default here is no colorkey
			else
				m_pHandle->blit_fast(*SrcSurface.m_pHandle, DestX, DestY, src_rect);
		}
		else
		{
			// Set new dest rect
			Rect dest_rect(DestX, DestY, DestX + src_rect.width(), DestY + src_rect.height());
			// Do Clipping of rectangles
			if(!clip_releated_src_dest_rectangles(*this, SrcSurface, dest_rect, src_rect))
				return;
			// Check values for 0 size
			if (dest_rect.height() == 0 || dest_rect.width() == 0)
				return;
			std::uint_least32_t dd_flags = 0;
			// Set color keying if any
			if(Flags & BF_USE_SRC_COLORKEY )
				dd_flags |= DDBLT_KEYSRC;
			// Use mirror
			if(Flags & BF_MIRROR_Y)
				m_pHandle->blit_mirror_y(*SrcSurface.m_pHandle, src_rect, dest_rect, dd_flags);
			else
				m_pHandle->blit(*SrcSurface.m_pHandle, src_rect, dest_rect, dd_flags);
		}
		// Update backup
		m_pHandle->try_update_backup();
	}

	VideoSurfaceRegion VideoSurface::get_region(std::uint_least16_t /*RegionId*/) const
	{
		JA2_ASSERT(false);
		return VideoSurfaceRegion();
	}

	Rect VideoSurface::get_rect() const
	{
		return Rect(0, 0, width(), height());
	}

	bool VideoSurface::clip_releated_src_dest_rectangles(VideoSurface &DestSurface, VideoSurface &SrcSurface, Rect &DestRect, Rect &SrcRect)
	{
		// Check for invalid start positions and clip by ignoring blit
		if ( DestRect.left() >= DestSurface.width() || DestRect.top() >= DestSurface.height())
			return false;
		if ( SrcRect.left() >= SrcSurface.width() || SrcRect.top() >= SrcSurface.height())
			return false;
		// For overruns
		// Clip destination rectangles
		if(DestRect.right() > DestSurface.width())
		{
			// Both have to be modified or by default streching occurs
			DestRect.set_right(DestSurface.width());
			SrcRect.set_right(SrcRect.left() + DestRect.width());
		}
		if(DestRect.bottom() > DestSurface.height())
		{
			// Both have to be modified or by default streching occurs
			DestRect.set_bottom(DestSurface.height());
			SrcRect.set_bottom(SrcRect.top() + DestRect.height());
		}
		// Clip src rectangles
		if(SrcRect.right() > SrcSurface.width())
		{
			// Both have to be modified or by default streching occurs
			SrcRect.set_right(SrcSurface.width());
			DestRect.set_right(DestRect.left() + SrcRect.width());
		}
		if(SrcRect.bottom() > SrcSurface.height())
		{
			// Both have to be modified or by default streching occurs
			SrcRect.set_bottom(SrcSurface.height());
			DestRect.set_bottom(DestRect.top() + SrcRect.height());
		}
/*x
		// For underruns
		// Clip destination rectangles
		if ( DestRect->left < 0 )
		{
			// Both have to be modified or by default streching occurs
			DestRect->left = 0;
			SrcRect->left = SrcRect->right - ( DestRect->right - DestRect->left );
		}
		if ( DestRect->top < 0 )
		{
			// Both have to be modified or by default streching occurs
			DestRect->top = 0;
			SrcRect->top = SrcRect->bottom - ( DestRect->bottom - DestRect->top );
		}

		// Clip src rectangles
		if ( SrcRect->left < 0 )
		{
			// Both have to be modified or by default streching occurs
			SrcRect->left = 0;
			DestRect->left = DestRect->right  - ( SrcRect->right - SrcRect->left );
		}
		if ( SrcRect->top < 0 )
		{
			// Both have to be modified or by default streching occurs
			SrcRect->top = 0;
			DestRect->top = DestRect->bottom - ( SrcRect->bottom - SrcRect->top );
		}
*/
		return true;
	}

	VideoSurface::MemoryUsage VideoSurface::get_memory_usage() const
	{
		return m_MemoryUsage;
	}

	void VideoSurface::flip()
	{
		m_pHandle->flip();
	}
} /*sgp*/} /*ja2*/